/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2011.03.30                                          *
 *                                                                       *
 *************************************************************************/

#ifndef FreeMAPs_include_COMMON_tbox_Utilities_h
#define FreeMAPs_include_COMMON_tbox_Utilities_h

#include "FreeMAPsConfig.h"

//C++ headers
#include <vector>
#include <list>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <typeinfo> 

//COMMON headers
#include "COMMON/base/Common.h"
#include "COMMON/tbox/StringToEnum.h"

namespace FreeMAPs
{

//forward declarations
class InputDatabase;

/*
typedef int mode_t;

#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_IRUSR 0
#define S_IWUSR 0
#define S_IXUSR 0
*/

//forward declarations
class Point;
/**
 * The Utilities namespace is for functions
 * which are useful but don't necessarily belong anywhere else.
 */
namespace Utilities
{

/**
 * in libMesh, many int type may have a type of id_types,
 * so we cast it to we want from int.
 */
template<typename T>
inline
std::set<T>
type_cast(
    const std::set<int>& src);

template<typename T>
inline
std::vector<T>
type_cast(
    const std::vector<int>& src);

/**
 * the file operators.
 */
//bool
//is_link_file (
//    const char* name);

//bool
//is_dir(
//    const char* name);

//bool
//is_file(
//    const char* name);

bool
can_access(
    const char* name);

bool
can_modify (
    const char* name);

/**
 * when write file to disk, check whether the file is exist already.
 * if not exist, do nothing.
 * if exist, given warning information depend on warning level.
 */
void
check_file_name (
    std::string& file_name);

/**
 * \p Utility::iota is a duplication of the SGI STL extension
 * \p std::iota.  It simply assigns sequentially increasing values
 * to a range. That is, it assigns \p value to \p *first, \p value + 1
 * to \p *(first + 1) and so on. In general, each iterator \p i in the
 * range [first, last) is assigned \p value +(i - \p first).
 */
template <typename ForwardIter, typename T>
inline void
iota (
    ForwardIter first,
    ForwardIter last,
    T value);

/**
 * Convert an integer to a string.
 *
 * The returned string is padded with zeros as needed so that it
 * contains at least the number of characters indicated by the
 * minimum width argument.  When the number is positive, the
 * string is padded on the left. When the number is negative,
 * the '-' sign appears first, followed by the integer value
 * padded on the left with zeros.  For example, the statement
 * int_to_string(12, 5) returns "00012" and the statement
 * int_to_string(-12, 5) returns "-0012".
 */
std::string
int_to_string(
    int num,
    int min_width = 1);


/**
 * usually used in destructor function,
 * free space like std::map<key, Value*> map_object.
 * delete all the value's point.
 */
template <typename Key, typename Value, typename Comp>
void
clear_pointer_object (
    std::map<Key, Value*, Comp>& obj);

template <typename T>
void
clear_pointer_object (
    std::vector<T*>& obj);

template <typename T>
void
clear_pointer_object (
    std::list<T*>& obj);

template <typename T>
void
clear_pointer_object (
    T* obj);

/**
 * @return true iff a string is consist of space.
 */
inline
bool
is_blank_string(
    const std::string& string);

/**
 * compare two string and return the number of same chars in the two string from begin.
 * for example, get_num_of_same_chars (abc, sabc)=0
 * get_num_of_same_chars (abc, abef)=2
 */
int
get_num_of_same_chars (
    const std::string& str1,
    const std::string& str2);

/**
 * if given file is exist in the disk, build a new one
 * that does not exist by adding a suffix.
 */
//std::string
//get_new_file_name (
//    const std::string& origin_file_name, const std::string& suffix = "_");

/**
 * @brief create a new file that doesn't exist
 * @return true if create file successfully and false otherwise
 */
bool
open_file_for_write (
    const std::string& file_name,
    std::ofstream& os);

/**
 * @brief open an existed file, if not, create one
 * @return true if open file successfully and false otherwise
 */
bool
open_file_for_read (
    const std::string& file_name,
    std::ifstream& os);

/**
 * @brief close an openning pointer
 * @return true if close file successfully and false otherwise
 */
bool
close_file (
    std::ofstream& os);

bool
close_file (
    std::ifstream& os);

/**
 * @return the current directory name.
 */
std::string
get_current_directory_name();

/**
 * Create the directory specified by the path string so that
 * Utilities::recursive_mkdir can be called.
 * but the path will be parsered to remove "//" to "/" and remove the last "/",
 * and environment variable is supported.
 * so that path like "$HOME//sd/" can worked well.
 * if the file is a relative path, changed it to absolute path by add the directory of default_dir
 */
std::string
get_effective_file_name (
    const std::string& path,
    const std::string& default_dir);

/**
 * parser the file name if the name is a absolute one
 * such as "/home/user/file"
 * then it will return dir_name="/home/user" and file_name="file"
 */
void
parser_file_name (
    const std::string& origin_file_name,
    std::string& dir_name,
    std::string& file_name);

/**
 * a simple regular expression match utility.
 * if the str=pattern+rest_str(from_begin) or
 * str=rest_str+pattern (!from_begin), return false.
 * blank symbol and \t will be ignored.
 * we do not use regex.h, because we want to get rest_str
 * for the other resean.
 */
bool
regular_match(
    const std::string& str,
    const std::string& pattern,
    const bool from_begin,
    std::string& rest_str);

/**
 * @return a string with the blank
 * mode -2: delete all blank aronund "/"
 * mode -1: delete all the blank
 * mode  0: delete the blank in front and back
 * mode  1: delete the front blank
 * mode  2: delete the back blank
 */
std::string
get_string_without_blank(
    const std::string& str, int mode);

/**
 * delete all blank symbol around char c
 * this is used to delete the blank symbols in a directory string
 * so that make "/ example tree/file" to "/example tree/file "
 */
std::string
delete_blank_around_char(
    const std::string& str, const char c = '/');

/**
 * @return true if the char is a blank
 * such as ' ', '\t', '\n'
 */
bool
is_blank_char(
    const char c);

/**
 * make directory in a recursive way.
 */
//void
//recursive_mkdir(
//    const std::string& path,
//    mode_t mode = (S_IRUSR | S_IWUSR | S_IXUSR),
//    bool only_node_zero_creates = true);

/**
 * a implement of RestartManager::write_restart_file
 * so that write single file can be supported.
 */
void
write_restart_file (
    const std::string& restart_write_dir,
    const int step);

/**
 * @return a variable's nondimensional value, that means return dimensional_value/characteristic_scale.
 * the variable's characteristic scale is saved in the variable itself accessed through
 * VariableInfo by calling it's symbol.
 * error will be reported if var_symbol is not found in the VariableInfo.
 */
double
get_nondimensional_value_by_name (
    const std::string& var_name,
    const double dimensional_value,
    const bool do_check);

/**
 * similar to the get_nondimensional_value(), deal with an array.
 */
std::vector<double>
get_nondimensional_vector_by_name (
    const std::string& var_name,
    const std::vector<double>& dimensional_array,
    const bool do_check);

/**
 * similar to the get_nondimensional_value(), but do not return a new array, just modify the input array.
 */
void
nondimensionalize_vector_by_name (
    const std::string& var_name,
    std::vector<double>& dimensional_array,
    const bool do_check);

void
nondimensionalize_point (
    Point& dimensional_point,
    const bool do_check);

/**
 * @return a variable's dimensional value, that means return nondimensional_value * characteristic_scale.
 * the variable's characteristic scale is saved in the variable itself accessed through
 * VariableInfo by calling it's symbol.
 * error will be reported if var_symbol is not found in the VariableInfo.
 */
double
get_dimensional_value_by_name (
    const std::string& var_name,
    const double nondimensional_value,
    const bool do_check);

/**
 * similar to the get_dimensional_value(), deal with an array.
 */
std::vector<double>
get_dimensional_vector_by_name (
    const std::string& var_sname,
    const std::vector<double>& nondimensional_array,
    const bool do_check);

/**
 * similar to the get_dimensional_vector(), but do not return a new array, just modify the input array.
 */
void
dimensionalize_vector_by_name (
    const std::string& var_name,
    std::vector<double>& nondimensional_array,
    const bool do_check);

double
get_dimensional_volume (
    const double nondim_vol);

double
get_nondimensional_volume (
    const double dim_vol);

inline int
get_num_of_nodes_in_cell (
    const int dim);

/**
 * arrange string set as str1, str2, ..., strn for output
 * @param array: the elems lies in.
 * @param add_quot_mark: set str as str or "str"
 * @param num_in_one_line: the elems number in one line. a symbol of '\n' will be add to the line end.
 */
template <typename T, typename Cmp>
std::string
get_rearranged_set (
    const std::set<T, Cmp>& array,
    const bool add_quot_mark,
    const int num_in_one_line);

/**
 * arrange string array as str1, str2, ..., strn for output
 */
template <typename T>
std::string
get_rearranged_vector (
    const std::vector<T>& array,
    const bool add_quot_mark,
    const int num_in_one_line);

template <typename T>
std::string
get_rearranged_array (
    const T* array,
    const int size_array,
    const bool add_quot_mark,
    const int num_in_one_line);

/**
 * arrange man as:
 * (key_1, value_1)
 * (key_2, value_2)
 *  ... ...
 * (key_n, value_n)
 */
template <typename Key, typename Value, typename Cmp>
std::string
get_rearranged_map(
    const std::map<Key, Value, Cmp>& map);

/**
 * simular to get_rearranged_vector, but all items in the array is a enum item.
 * so translate it to string.
 */
template <typename T>
std::string
get_rearranged_enum_vector(
    const std::vector<T>& array,
    const bool add_quot_mark,
    const int num_in_one_line);

/**
 * fill the origin_array by arr, if not fill all, use 0 to fill the left.
 * for examples, arr=[a1, a2], origin_array=[0,0,0]
 * after filling, origin_array=[a1, a2, 0]
 */
template<typename T>
inline void
fill_vector (
    const int dim,
    const T* arr,
    T* origin_array);

/**
 * insert the src set to destinate set.
 */
template<typename T, typename Cmp>
inline void
insert (
    const std::set<T, Cmp>& src,
    std::set<T, Cmp>& des);

/**
 * insert all the string in the array into the destinate string
 * this is mostly used when doing keys checking.
 */
template<typename T, typename Cmp>
inline void
insert (
    const std::vector<T>& src,
    std::set<T, Cmp>& des);

/**
 * @return the combinated set
 */
template<typename T, typename Cmp>
inline std::set<T, Cmp>
get_combined_set(
    const std::set<T, Cmp>& s1,
    const std::set<T, Cmp>& s2);

template<typename Key, typename Value, typename Cmp>
inline std::map<Key, Value, Cmp>
get_combined_map(
    const std::map<Key, Value, Cmp>& m1,
    const std::map<Key, Value, Cmp>& m2);

/**
 * when read information from database, spelling mistake may occur in string.
 * give the most similar string in the allowable tables
 * if the two similar string has same alpha more than asked number, return true.
 * if not find, return false.
 */
bool
get_similar_string(
    const std::string& wrong_string,
    const std::set<std::string>& allowable_table,
    std::string& similar_string,
    const int same_length = 3);

/**
 * @return the lower of the integrated two points
 * that means p=(min (p1.x, p2.x), min (p1.y, p2.y), min (p1.z, p2.z))
 */
Point
get_integrated_lower_point (
    const Point& p1,
    const Point& p2);

/**
 * @return the upper of the integrated two points
 * that means p=(max(p1.x, p2.x), max(p1.y, p2.y), max(p1.z, p2.z))
 */
Point
get_integrated_upper_point (
    const Point& p1,
    const Point& p2);

/**
 * return true if the name is one of ExprEval lib's reserved function name.
 */
bool
is_expreval_lib_reserved_function_name (
    const std::string& name);

/**
 * check if all the variables in expression
 * is setted in variable_info
 */
bool
get_symbols_from_expression (
    const std::string& expr,
    std::set<std::string>& symbols);

/**
 * compute the areas and volume of
 * a cell with edge dx.
 * the aeras is the surface that is orthorized to dx.
 */
inline void
compute_vol_and_areas_of_cell(
    const int dim,
    const double* dx,
    double& vol,
    double* areas);

/**
 * compute the volume of
 * a cell with edge dx
 * it is a nondimensional value.
 */
inline double
get_cell_vol (
    const int dim,
    const double* dx);

/**
 * compute the areas of
 * a cell with edge dx
 */
inline void
compute_areas_of_cell(
    const int dim,
    const double* dx,
    double* areas);

/**
 * return a cell's surface area that perpendularity to
 * the direction
 */
inline double
get_area_of_cell(
    const int dim,
    const int direction,
    const double* dx);

/**
 * if a data is distributed in all processors
 * and on proc[i] it has proc_nums[i] numbers,
 * given a number, this function will tell which
 * processor it lies.
 * for example, if proc_nums = [2, 4, 5, 6]
 * the if number = 1, it return 0
 * if number =5, it return 2
 */
inline int
get_processor_id (
    const int number,
    const std::vector<int>& proc_nums);

/**
 * we use a letter of L(ower),C(enter),U(pper) to label the position.
 * for example, a left cell will be labeled (LCC)(x-lower, y-center, z-center).
 * but use L, C, U is not convient to do some judgement.
 * so trans L to -1, C to 0 and U to +1 for some convient.
 */
inline int
trans_position_to_int (
    const char position);

inline double
degree2rad (
    const double angle_in_degree);

inline double
rad2degree(
    const double angle_in_rad);

/**
 * used to check enum type value if it is INVALID_VALUE
 * @note that the enum's list must have INVALID_VALUE as it's last item
 */
template<typename T>
inline bool
is_invalid_value (
    const T value);

/**
 * translate std::vector<T> to std::set<T>
 */
template<typename T>
inline std::set<T>
trans_vec_to_set (
    const std::vector<T>& array);

/**
 * translate std::set<T> to std::vector<T>
 */
template<typename T, typename Cmp>
inline std::vector<T>
trans_set_to_vec (
    const std::set<T, Cmp>& set);

template<typename T>
inline void
scale (
    std::vector<T>& vec,
    const T factor);

/**
 * extract all keys from a map to a set
 */
template<typename Key_type, typename Value_type, typename Cmp>
inline std::set<Key_type, Cmp>
get_map_keys (
    const std::map<Key_type, Value_type, Cmp>& map);

/**
 * when get values from input file, enum values can NOT be valued arbitrary.
 * this will return the acceptable table of the allowable values of a enum type
 */
template<typename T>
std::set<std::string>
get_acceptable_enum_values();

/**
 * @return a random point in the box whose cent and edge are known.
 */
Point
get_random_point_in_box (
    const Point& cent_point,
    const double* dx,
    const int dim);

double
interpolation_by_distance_weight(
    const std::vector<std::vector<double> >& data);

/**
 * add a reduced tensor to a tensor of 3*3
 * the tensor is type of:
 *    xx xy xz
 *    yx yy yz
 *    zx zy zz
 * a reduced tensor in 2D is: xx yy xy
 * in 2D axisymmetry is: [xx,yy,xy,zz]
 * in 3D is: xx yy zz xy yz zx
 */
template <typename T>
void
add_reduced_tensor (
    std::vector<T>& tensor,
    const int start_index,
    const std::vector<T>& reduced_tensor);

template<typename T>
int
find_in_vector(
    const std::vector<T>& src,
    const T dst);

} //namespace Utilities

} //namespace FreeMAPs

#include "COMMON/tbox/Utilities.inl"

#endif
